package de.fhd.pms.dao;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import de.fhd.pms.Rechnung;
import de.fhd.pms.dto.FahrtDTO;
import de.fhd.pms.dto.RechnungDTO;
import de.fhd.pms.model.Auto;
import de.fhd.pms.model.Fahrt;
import de.fhd.pms.model.Kunde;
import de.fhd.pms.model.Station;

/**
 * The Data access class for {@link Fahrt} objects. DAO encapsulates all
 * accesses to the data source, including construction and dismantling of the
 * connection. All Interaction with the database regarding the entity bean
 * {@link Fahrt} should be handled by this class! Its tasks:
 * <ul>
 * <li>set new objects of the class</li>
 * <li>modify or delete an object of the class</li>
 * <li>deliver all objects of the class</li>
 * <li>further functions in context to the class objects</li>
 * </ul>
 * 
 * @author Josef Fischer
 * @author Alexander Neetzel
 * @author Philipp Six
 */

public class FahrtDao extends HibernateDaoSupport {

	/**
	 * <p>
	 * returns all {@link Fahrt} objects, which are not yet termiated, i.e.
	 * which have a ankunft property == null. All the results are returned as
	 * {@link FahrtDTO} objects.
	 * </p>
	 * <p>
	 * In order to access lazy loaded properties, a HibernateCallback is used.
	 * </p>
	 * 
	 * @return A sorted set of {@link FahrtDTO} objects
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<FahrtDTO> findUnfinished() {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<FahrtDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(Fahrt.class);
				criteria.add(Restrictions.isNull("ankunft"));
				List<Fahrt> unfinishedFahrt = criteria.list();
				return createDtoSet(unfinishedFahrt);
			}
		});
	}

	/**
	 * Returns a single {@link Fahrt} object by its id
	 * 
	 * @param id
	 *            the primary key of {@link Fahrt}
	 * @return a single {@link Fahrt}
	 */
	public Fahrt findById(int id) {
		HibernateTemplate template = getHibernateTemplate();
		return (Fahrt) template.get(Fahrt.class, id);
	}

	/**
	 * <p>
	 * returns all {@link Fahrt} objects as {@link FahrtDTO} objects.
	 * </p>
	 * <p>
	 * In order to access lazy loaded properties of those, a HibernateCallback
	 * is used.
	 * </p>
	 * 
	 * @return A sorted set of {@link FahrtDTO} objects
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Set<FahrtDTO> findFinished() {
		HibernateTemplate template = getHibernateTemplate();
		return (Set<FahrtDTO>) template.execute(new HibernateCallback() {
			@Override
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(Fahrt.class);
				criteria.add(Restrictions.isNotNull("ankunft"));
				List<Fahrt> finishedFahrt = criteria.list();
				return createDtoSet(finishedFahrt);
			}
		});
	}

	/**
	 * Creates a DTO-Set of {@link Fahrt} objects
	 * 
	 * @param
	 * @return a Set of {@link Fahrt} objects
	 */
	private Set<FahrtDTO> createDtoSet(List<Fahrt> fahrten) {
		Set<FahrtDTO> results = new TreeSet<FahrtDTO>();
		for (Fahrt f : fahrten) {
			f.getAnkunft();
			results.add(new FahrtDTO(f.getId(), f.getAuto().getMarke(), f
					.getAuto().getModell(), f.getKunde().getVorname(), f
					.getKunde().getNachname(), f.getKm(), f.getAbfahrt(), f
					.getAnkunft(), f.getDauer(), f.getKosten(), f.getAuto()
					.getId(), f.getKunde().getId()));
		}
		return results;
	}

	/**
	 * Returns all {@link Fahrt} objects from the DB
	 * 
	 * @return a list of {@link Fahrt} objects
	 */
	@SuppressWarnings("unchecked")
	public List<Fahrt> findAll() {
		HibernateTemplate template = getHibernateTemplate();
		@SuppressWarnings("rawtypes")
		List allFahrt = template.loadAll(Fahrt.class);
		return (List<Fahrt>) allFahrt;
	}

	/**
	 * Starts a trip specifying a {@link Auto} id and a set of {@link Kunde}
	 * ids, and saves it in the DB
	 * 
	 * <p>
	 * Sets station null to indicate that the {@link Auto} is on a {@link Fahrt}
	 * </p>
	 * 
	 * @param autoId
	 * @param kundeId
	 */
	public void begin(int autoId, int kundeId) {
		HibernateTemplate template = getHibernateTemplate();
		Auto auto = (Auto) template.get(Auto.class, autoId);
		Kunde kunde = (Kunde) template.get(Kunde.class, kundeId);
		Fahrt fahrt = new Fahrt();
		auto.setStation(null);
		fahrt.setAuto(auto);
		fahrt.setKunde(kunde);
		template.saveOrUpdate(fahrt);
		template.saveOrUpdate(auto);
	}

	/**
	 * Ends a trip and saves it into the DB
	 * 
	 * <ul>
	 * <li>sets the arrival date of the {@link Fahrt}</li>
	 * <li>sets the return station into the {@link Auto} object</li>
	 * <li>sets the duration of the {@link Fahrt}</li>
	 * <li>sets the driven km of the {@link Fahrt}</li>
	 * <li>sets the costs of the {@link Fahrt}</li>
	 * </ul>
	 * 
	 * @param fahrtId
	 * @param stationId
	 * @param km
	 */
	public void end(int fahrtId, int stationId, float km) {
		HibernateTemplate template = getHibernateTemplate();
		Fahrt fahrt = (Fahrt) template.get(Fahrt.class, fahrtId);
		Auto auto = (Auto) template.get(Auto.class, fahrt.getAuto().getId());
		Station station = (Station) template.get(Station.class, stationId);

		fahrt.setAnkunft(new Date());
		auto.setStation(station);
		fahrt.setDauer(fahrt.getAbfahrt(), fahrt.getAnkunft());
		fahrt.getDauer();
		fahrt.setKm(km);
		fahrt.setKosten(fahrt.getDauer(), fahrt.getKm());
		auto.setKm(auto.getKm() + km);
		template.saveOrUpdate(fahrt);
		template.saveOrUpdate(auto);
	}

	/**
	 * Creates an invoice for a specific {@link Fahrt} object. Its content:
	 * 
	 * <ul>
	 * <li>It casts all required data as String data type</li>
	 * <li>Creates a new instance of {@link RechnungDTO} object</li>
	 * <li>Generates a PDF invoice by handing over the {@link RechnungDTO}
	 * object</li>
	 * </ul>
	 * 
	 * @param fahrt
	 * @return rechnung
	 * 
	 */
	public RechnungDTO getInvoice(Fahrt fahrt) {
		int id = fahrt.getId();
		String marke = fahrt.getAuto().getMarke();
		String modell = fahrt.getAuto().getModell();
		String tarif = fahrt.getAuto().getTarif();
		String baujahr = fahrt.getAuto().getBaujahr();
		String farbe = fahrt.getAuto().getFarbe();
		String kennzeichen = fahrt.getAuto().getKennzeichen();
		String fahrtId = "" + fahrt.getId();
		String abfahrt = "" + fahrt.getAbfahrt();
		String ankunft = "" + fahrt.getAnkunft();
		String dauer = "" + fahrt.getDauer();
		String km = "" + fahrt.getKm();
		float kosten = fahrt.getKosten();
		String name = fahrt.getKunde().getVorname() + " "
				+ fahrt.getKunde().getNachname();
		String strasse = fahrt.getKunde().getStrasse();
		String ort = fahrt.getKunde().getPlz() + " "
				+ fahrt.getKunde().getOrt();
		String telefon = fahrt.getKunde().getTelefon();
		RechnungDTO rechnung = new RechnungDTO(id, marke, modell, tarif,
				baujahr, farbe, kennzeichen, fahrtId, abfahrt, ankunft, dauer,
				km, kosten, name, strasse, ort, telefon);
		Rechnung r = new Rechnung(rechnung);
		return rechnung;
	}
}
